﻿using System;
using System.Collections.Generic;
using System.Linq;
using HPCloud.Objects.Domain.Compute.Servers.Actions;
using HPCloud.Objects.Domain.Compute.Servers;
using HPCloud.Common.Properties;
using System.Threading;
using HPCloud.Objects.Domain.Compute.Security;
using HPCloud.Objects.Domain.Compute;
using HPCloud.Objects.Domain.Compute.Networking;
using HPCloud.Objects.DataAccess.Security;

namespace HPCloud.Objects.DataAccess.Compute
{
    public class NonWindowsInstanceBuilder
    {      
        BaseRepositoryFactory _repositoryFactory = null;
        Settings _settings;
        public delegate void CreateInstanceEventHandler(object sender, CreateInstanceEventArgs e);
        public event CreateInstanceEventHandler Changed;
        const string cSSHSecurityGroupName = "sshGroup";
//=========================================================================================
/// <summary>
/// 
/// </summary>
//=========================================================================================
        public NonWindowsInstanceBuilder(BaseRepositoryFactory repositoryFactory, Settings settings)
        {
            _settings = settings;
            _repositoryFactory = repositoryFactory;
        }
//=========================================================================================
/// <summary>
/// 
/// </summary>
//=========================================================================================
        public NonWindowsInstanceBuilder()
        { }
//=========================================================================================
/// <summary>
/// 
/// </summary>
/// <param name="e"></param>
//=========================================================================================
        private void OnChanged(CreateInstanceEventArgs e)
        {
            if (Changed != null)
                Changed(this, e);
        }
//=========================================================================================
/// <summary>
/// 
/// </summary>
//=========================================================================================
        public Settings Settings
        {
            get { return _settings; }
            set { _settings = value; }
        }
//=========================================================================================
/// <summary>
/// 
/// </summary>
//=========================================================================================
        public BaseRepositoryFactory RepositoryFactory
        {
            get { return _repositoryFactory; }
            set { _repositoryFactory = value; }
        }
//=========================================================================================
/// <summary>
/// 
/// </summary>
/// <param name="message"></param>
//=========================================================================================
        private void WriteSection(string headerText)
        {
            ConsoleColor oldColor = Console.ForegroundColor;
            Console.WriteLine(" ");
            Console.ForegroundColor = ConsoleColor.DarkGray;
            Console.WriteLine("==============================================================================================================================================");
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine(headerText);
            Console.ForegroundColor = ConsoleColor.DarkGray;
            Console.WriteLine("==============================================================================================================================================");
            Console.ForegroundColor = oldColor;
        }
//=========================================================================================
/// <summary>
/// 
/// </summary>
//=========================================================================================
        private void ShowPasswordBasedLoginInstructions(string adminPassword, string userName)
        {
            this.WriteSection("Password Based Remote Login Instructions");
            Console.WriteLine("In addition to the key based authentication method you can also login via Username and Password");
            Console.WriteLine("");
            Console.WriteLine("1. You have been assigned the following Administrator password for this instance : " + adminPassword);
            Console.WriteLine("2. The username for this password is " + userName);
            Console.WriteLine("");
        }
//=========================================================================================
/// <summary>
/// 
/// </summary>
//=========================================================================================
        private void ShowKeyBasedLoginInstructions(string keyname)
        {
            this.WriteSection("Key Based Remote Login Instructions");
            Console.WriteLine("1. Make sure that you have downloaded the latest version of Putty and PuttyGen for Windows.");
            Console.WriteLine("2. Make note of the fully qualified path to Putty.exe. You will be prompted for this the first time you attempt to open an SSH session.");
            Console.WriteLine("3. Tell PuttyGen to Load an existing key file. This file will be located at " + this.Settings.KeyStoragePath + @"\HP\" + keyname + ".pem");
            Console.WriteLine("4. Convert the .pem file to the .ppk format that Putty.exe understands by clicking the Save Private Key button.");
            Console.WriteLine("5. Save the .ppk file as " + keyname + ".ppk");
            Console.WriteLine("6. Launch the SSH session with the Connect-Server cmdlet");
            Console.WriteLine("7. HINT : If you use the same keypair name for each Server you create you can skip steps 1-5 after the steps have been completed once.");
            Console.WriteLine("");
        }
//=========================================================================================
/// <summary>
/// Ensures that Port 22 is open for business..
/// </summary>
//=========================================================================================
        private void CreateSSHPortRule(string groupId)
        {
            ISecurityRepository repository = this.RepositoryFactory.CreateSecurityRepository();
            NewSecurityRule rule = new NewSecurityRule();

            rule.FromPort      = "22";
            rule.ToPort        = "22";
            rule.Name          = "sshRule";
            rule.IpProtocol    = "tcp";
            rule.ParentGroupId = groupId;

            SecurityGroupRule newRule = repository.SaveSecurityRule(rule);
        }
//=========================================================================================
/// <summary>
/// This will create the SSH SG if it doesn't yet exist.
/// </summary>
//=========================================================================================
        private SecurityGroupAssignment CreateSSHecurityGroupAssignment()
        {
            ISecurityRepository repository     = this.RepositoryFactory.CreateSecurityRepository();
            List<SecurityGroup> groups         = repository.GetSecurityGroups();
            SecurityGroupAssignment assignment = null;

            // The SSH Security Group already exist..

            if (groups.Where(g => g.Name.Contains(cSSHSecurityGroupName)).Any())
            {
                this.OnChanged(new CreateInstanceEventArgs(""));
                this.OnChanged(new CreateInstanceEventArgs("- Assigning pre-existing SSH Security Group."));
                assignment = new SecurityGroupAssignment(cSSHSecurityGroupName);
            }
            else
            {
                // We need to create a new RDP Security Group so the User can Remote Desktop into this new instance..

                NewSecurityGroup group = new NewSecurityGroup();
                group.Description      = "Security Group enabling the SSH Protocol";
                group.Name             = cSSHSecurityGroupName;

                this.OnChanged(new CreateInstanceEventArgs("- Creating new SSH Security Group and SSH Port Rules."));
                SecurityGroup sshSecurityGroup = repository.SaveSecurityGroup(group);
                this.CreateSSHPortRule(sshSecurityGroup.Id);

                this.OnChanged(new CreateInstanceEventArgs("- Assigment complete."));
                assignment = new SecurityGroupAssignment(cSSHSecurityGroupName);
            }
            return assignment;
        }
//=========================================================================================
/// <summary>
/// 
/// </summary>
/// <param name="server"></param>
//=========================================================================================
        public void CreateInstance(NewServer server, string keyName)
        {
            ISecurityRepository securityRepository = this.RepositoryFactory.CreateSecurityRepository();
            IServerRepository serverRepository     = this.RepositoryFactory.CreateServerRepository();

            // Make sure the instance is associated with the SSH SecurityGroup, opening up the correct ports..

            server.SecurityGroups.Add(this.CreateSSHecurityGroupAssignment());

            // Create the instance itself..

            this.OnChanged(new CreateInstanceEventArgs("- Creating Server Instance."));
            HPCloud.Objects.Domain.Compute.Server result = serverRepository.SaveServer(server);
            this.OnChanged(new CreateInstanceEventArgs("- Instance created successfully."));

            // Save the new Credentials and tell the User how to connect...

            this.OnChanged(new CreateInstanceEventArgs("- Saving Credentials."));
            CredentialListManager credManager = new CredentialListManager(this.Settings);
            credManager.SetPassword(result.Id, result.AdminPassword);
            this.OnChanged(new CreateInstanceEventArgs("- Credentials saved."));
            
            // Show the User some login instructions..
            
            if (server.KeyName != null && server.KeyName != String.Empty)
               this.ShowKeyBasedLoginInstructions(keyName);

            //ShowPasswordBasedLoginInstructions(result.AdminPassword, "ubuntu");
        }

    }
}
